#include "PhysicsWorld.h"
#include "Global.h"
#include "3ds.h"
#include "KiraGameController.h"

extern int ymin;
extern int ymax;
extern int xmin;
extern int xmax;
extern int zmin;
extern int zmax;
extern KiraGameController *gameController;

bool myContactProcessedCallback(btManifoldPoint& cp, void* body0,void* body1)
{
	btRigidBody *obj0 = (btRigidBody *)body0;
	btRigidBody *obj1 = (btRigidBody *)body1;
	NodeInfo *ni0 = (NodeInfo *)obj0->getUserPointer();
	NodeInfo *ni1 = (NodeInfo *)obj1->getUserPointer();
	gameController->DetectCollision(ni0->name, ni1->name);
	return false;
}

PhysicsWorld * PhysicsWorld::m_instance = NULL;

PhysicsWorld::PhysicsWorld(void)
{
	InitPhysics();
}

PhysicsWorld::~PhysicsWorld(void)
{
	ExitPhysics();
}

void PhysicsWorld::InitPhysics()
{
	m_collisionConfiguration = new btDefaultCollisionConfiguration();
	m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
	m_overlappingPairCache = new btDbvtBroadphase();
	btSequentialImpulseConstraintSolver* sol = new btSequentialImpulseConstraintSolver;
	m_solver = sol;
	m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
	m_dynamicsWorld->setGravity(btVector3(0, 0, 0));
	//mNumEntitiesInstanced = 0;
	gContactProcessedCallback = myContactProcessedCallback;

	//in physics
	{
		//ymin
		btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(0,1,0),0);
		btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,ymin,0)));
		btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
		btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
		m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
		NodeInfo *ni = new NodeInfo("bound");
		boundRigidBody->setUserPointer((void *)ni);
		boundRigidBody->setRestitution(0.8);
	}
	//{
	//	//ymax
	//	btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(0,-1,0),0);
	//	btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,ymax,0)));
	//	btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
	//	btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
	//	m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
	//	NodeInfo *ni = new NodeInfo("bound");
	//	boundRigidBody->setUserPointer((void *)ni);
	//	boundRigidBody->setRestitution(0.8);
	//}
	//{
	//	//xmin
	//	btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(1,0,0),0);
	//	btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(xmin,0,0)));
	//	btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
	//	btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
	//	m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
	//	NodeInfo *ni = new NodeInfo("bound");
	//	boundRigidBody->setUserPointer((void *)ni);
	//	boundRigidBody->setRestitution(0.8);
	//}
	//{
	//	//xmax
	//	btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(-1,0,0),0);
	//	btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(xmax,0,0)));
	//	btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
	//	btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
	//	m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
	//	NodeInfo *ni = new NodeInfo("bound");
	//	boundRigidBody->setUserPointer((void *)ni);
	//	boundRigidBody->setRestitution(0.8);
	//}
	//{
	//	//zmin
	//	btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(0,0,1),0);
	//	btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,zmin)));
	//	btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
	//	btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
	//	m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
	//	NodeInfo *ni = new NodeInfo("bound");
	//	boundRigidBody->setUserPointer((void *)ni);
	//	boundRigidBody->setRestitution(0.8);
	//}
	//{
	//	//zmax
	//	btCollisionShape* boundShape = new btStaticPlaneShape(btVector3(0,0,-1),0);
	//	btDefaultMotionState* boundMotionState = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1),btVector3(0,0,zmax)));
	//	btRigidBody::btRigidBodyConstructionInfo boundRigidBodyCI(0,boundMotionState,boundShape);
	//	btRigidBody* boundRigidBody = new btRigidBody(boundRigidBodyCI);
	//	m_dynamicsWorld->addRigidBody(boundRigidBody, COL_BOUND, boundCollidesWith);
	//	NodeInfo *ni = new NodeInfo("bound");
	//	boundRigidBody->setUserPointer((void *)ni);
	//	boundRigidBody->setRestitution(0.8);
	//}
}

void PhysicsWorld::ExitPhysics()
{
	for (int i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0; i--)
	{
		btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}
		m_dynamicsWorld->removeCollisionObject(obj);
		delete obj;	
	}
	for (int i=0; i<m_collisionShapes.size(); i++)
	{
		btCollisionShape* shape = m_collisionShapes[i];
		m_collisionShapes[i] = 0;
		delete shape;
	}
	delete m_dynamicsWorld;
	delete m_solver;
	delete m_dispatcher;
	delete m_overlappingPairCache;
	delete m_collisionConfiguration;
	m_collisionShapes.clear();
}

void PhysicsWorld::UpdatePhysics( float etime )
{
	m_dynamicsWorld->stepSimulation(etime);
}

btRigidBody * PhysicsWorld::CreateRigidBody( float mass, btTransform *trans, btCollisionShape *a_shape, short group, short mask, Ogre::SceneNode *node, int id ,string name )
{
	bool flag = true;
	for (int i=0;i<m_collisionShapes.size();i++)
	{
		if (m_collisionShapes[i] == a_shape)
		{
			flag = false;
			break;
		}
	}
	if (flag)
	{
		m_collisionShapes.push_back(a_shape);
	}

	bool isDynamic = (mass != 0.f);

	btVector3 localInertia(0,0,0);
	if (isDynamic)
		a_shape->calculateLocalInertia(mass,localInertia);

	btDefaultMotionState *myMotionState;
	myMotionState = (btDefaultMotionState *)new MyMotionState(*trans, node);

	btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,a_shape,localInertia);
	btRigidBody* body = new btRigidBody(rbInfo);

	m_dynamicsWorld->addRigidBody(body, group, mask);
	if (id>=0)
	{
		m_bodyMap.insert(pair<int, btRigidBody*>(id, body));
	}
	NodeInfo *ni = new NodeInfo(name);
	body->setUserPointer((void *)ni);

	return body;
}

btRigidBody * PhysicsWorld::GetRigidBody( int id )
{
	return m_bodyMap[id];
}

PhysicsWorld * PhysicsWorld::GetInstance()
{
	if(m_instance == NULL)
	{
		m_instance = new PhysicsWorld();
	}

	return m_instance;
}

btCollisionShape * PhysicsWorld::ReadModelFromFile( char *filePath )
{
	C3DSModel temp3DS;
	temp3DS.Load(filePath);
	temp3DS.MakeBulletShapes();
	btCollisionShape *shape = temp3DS.GetModel()->pObject[0].trimeshShape;
	return shape;
}
